<?php
/*************
#XML操作类
#lsj 2009-5-4
*************/
if(!defined('APP_HOME')) exit('Access Denied');
class lib_class_xml
{
    protected $parser;#XML解析器的资源标识
    protected $parent;#父节点名称
    protected $stack;#堆栈
    protected $file;#XML的内容(或URL)
    protected $charset;#输出的字符编码
    protected $lastTag;#当前节点名称
	protected $outXml = '';#将数组编译出的XML内容
	protected $outArr = array();#解释出的数组

    /*
    #构造函数
    #进行初始设置
    */
    public function __construct(&$text = null, $charset='UTF-8', $fold=false, $white=false)
	{
        $this->create();
        $this->setXml(&$text, $charset='UTF-8', $fold=false, $white=false);
    }

	/*
	#设置XML的内容(或URL)、输出的编码、是否大小写折叠、是否略过由白空字符组成的值
	#$text: XML的内容(或URL)
	#$charset: 解析器输出的字符编码是(ISO-8859-1，UTF-8，US-ASCII)
	#$fold: 大小写折叠，false:保持原来大小写 true:大小定不敏感，统一为大写
	#white: 略过由空白字符组成的值
	*/
	public function setXml(&$text, $charset='UTF-8', $fold=false, $white=false)
	{
		if($text !== null)
			$this->file = &$text;
		if($charset !== null){
			xml_parser_set_option(&$this->parser, XML_OPTION_TARGET_ENCODING, $charset);
			$this->charset = $charset;
		}
		if($fold !== null)
			xml_parser_set_option(&$this->parser, XML_OPTION_CASE_FOLDING, $fold);
		if($white !== null)
			xml_parser_set_option(&$this->parser, XML_OPTION_SKIP_WHITE, $white);

	}

    /*
	#解析XML
	#返回数组
	*/
	public function parse(&$xml='')
	{
        if(!is_resource(&$this->parser)){
	       $this->create();
	    }
        $this->outArr = array();
        $this->stack = array();
        $this->parent = &$this->outArr;

		if($xml != '') $this->file = &$xml;
		if(is_file($this->file) || eregi("^([a-z]+)://", $this->file)){
            $fp = @fopen($this->file, 'r');
			if($fp){
				while($data = fread($fp, 4096)){
					if(!xml_parse(&$this->parser, &$data, feof($fp))){
						$this->errno = 1;
                        $this->free();
						return false;
					}
				}
				return $this->outArr;
			}else{
				$this->errno = 2;
                $this->free();
				return false;
			}

		}elseif($this->file != ''){
			if(!xml_parse($this->parser, $this->file, true)){
				$this->errno = 1;
                $this->free();
				return false;
			}
            $this->free();
            return $this->outArr;
		}

		$this->errno = 3;
        $this->free();
		return false;
	}

	/*
	#编译数组为XML
	#返回XML
	*/
	public function compile($arr, $tag='')
	{
		if(!is_array($arr)) return false;
		$LT = "\t";
		$LN = "\n";
		$this->outXml .= '<?xml version="1.0" encoding="'.$this->charset.'" ?>'.$LN;
		if(count($arr) > 1 && !is_array(current($arr))){
			if($tag != ''){
				$prop = '';#属性
				$text = '';#内容
				foreach($arr as $key=>$val){
					if(is_numeric($key)) $text = '<![CDATA['.$val.']]>';
					else $prop .= ' '.$key.'="'.htmlspecialchars(addslashes($val)).'"';
				}
				if($text != '') $this->outXml .= $LT.'<'.$tag.$prop.'>'.$text.'</'.$tag.'>'.$LN;
				elseif($prop != '') $this->outXml .= $LT.'<'.$tag.$prop.' />'.$LN;
				else $this->outXml .= $LT.'<'.$tag.'></'.$tag.'>'.$LN;
			}else{
				foreach($arr as $key=>$val){
					$this->outXml .= $LT.'<'.$tag.'><![CDATA['.$val.']]></'.$tag.'>'.$LN;
				}
			}
			return $this->outXml;
		}else{
			foreach($arr as $key=>$val){
				$this->compile($val, $key);
			}
		}

		$this->errno = 4;
		return false;
	}

    /*
	#解析器初始化
	*/
    public function create()
    {
        $this->parser = &xml_parser_create();
        xml_parser_set_option(&$this->parser, XML_OPTION_CASE_FOLDING, false);
        xml_set_object(&$this->parser, &$this);
        xml_set_element_handler(&$this->parser, 'elementStart','elementEnd');
        xml_set_character_data_handler(&$this->parser, 'elementData');
    }

    /*
	#释放解析器
	*/
    public function free()
    {
       if(is_resource(&$this->parser)){
            xml_parser_free(&$this->parser);
       }
       return true;
    }

	/*
	#元素起始处理器
	*/
    protected function elementStart(&$parser, $tag, $attributes)
	{
        $this->data = '';
        $this->lastTag = $tag;
        if(is_array($this->parent) and array_key_exists($tag, $this->parent)){
            if(is_array($this->parent[$tag]) and array_key_exists(0, $this->parent[$tag])){
                $key = $this->countItems($this->parent[$tag]);
            }else{
                if(array_key_exists("$tag attr", $this->parent)){
                    $arr = array('0 attr'=>&$this->parent["$tag attr"], &$this->parent[$tag]);
                    unset($this->parent["$tag attr"]);
                }else{
                    $arr = array(&$this->parent[$tag]);
                }
                $this->parent[$tag] = &$arr;
                $key = 1;
            }
            $this->parent = &$this->parent[$tag];
        }else{
            $key = $tag;
        }
        if($attributes) $this->parent["$key attr"] = $attributes;
        $this->parent  = &$this->parent[$key];
        $this->stack[] = &$this->parent;
    }

	/*
	#元素数据处理器
	*/
    protected function elementData(&$parser, $data)
	{
        if($this->lastTag != NULL) $this->data .= $data;
    }

	/*
	#元素终止处理器
	*/
    protected function elementEnd(&$parser, $tag)
	{
        if($this->lastTag == $tag){
            $this->parent = $this->data;
            $this->lastTag = NULL;
        }
        array_pop($this->stack);
        if($this->stack) $this->parent = &$this->stack[count($this->stack)-1];
    }

	/*
	#计算数组数值型下标的个数
    #$array: 操作对象（数组）
	*/
	public function countItems(&$array)
	{
		return is_array($array) ? count(array_filter(array_keys($array), 'is_numeric')) : 0;
	}

	/*
	#获取错误信息
	#$is: 是否打印
	*/
	public function getError($is = 0)
	{
		if($this->errno === null) return null;

		$parseError = xml_error_string(xml_get_error_code(&$this->parser));
		$parseLine = xml_get_current_line_number(&$this->parser);

		$errors = array(
			1=>	'xml error: '.$parseError.' at line '.$parseLine,
			2=>	'xml file is not exists or cant be opened',
			3=>	'xml is not for empty',
			4=>	'array is not for empty',
		);

		if($is == 1 || $is == true || $is == 'on'){
			echo $errors[$this->errno];
		}
		return $errors[$this->errno];
	}

    /*
    #析构函数
    #释放解析器
    */
    public function __destruct()
	{
		$this->free();
	}

}



?>